<!DOCTYPE html>



  


<html class="theme-next muse use-motion" lang="zh-Hans">
<head><meta name="generator" content="Hexo 3.8.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT">





  <link rel="alternate" href="/atom.xml" title="renhao" type="application/atom+xml">






<meta name="description" content="参考了这篇文章学习了下，首先虚拟DOM的构成就是由在JSX组件中React.render调用时会调用React.createElement方法，这个方法会返回一个具有type、props等键值对的一个对象，这个对象就是虚拟DOM，然后ReactDOM.render按照相应的规则将其转化为真实的DOM节点。">
<meta property="og:type" content="article">
<meta property="og:title" content="react中的虚拟DOM">
<meta property="og:url" content="https://huangrenhao.gitee.io/2019/11/18/react-zhong-de-xu-ni-dom/index.html">
<meta property="og:site_name" content="renhao">
<meta property="og:description" content="参考了这篇文章学习了下，首先虚拟DOM的构成就是由在JSX组件中React.render调用时会调用React.createElement方法，这个方法会返回一个具有type、props等键值对的一个对象，这个对象就是虚拟DOM，然后ReactDOM.render按照相应的规则将其转化为真实的DOM节点。">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://huangrenhao.gitee.io/2019/11/18/react-zhong-de-xu-ni-dom/render-realize.png">
<meta property="og:updated_time" content="2020-09-03T08:50:36.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="react中的虚拟DOM">
<meta name="twitter:description" content="参考了这篇文章学习了下，首先虚拟DOM的构成就是由在JSX组件中React.render调用时会调用React.createElement方法，这个方法会返回一个具有type、props等键值对的一个对象，这个对象就是虚拟DOM，然后ReactDOM.render按照相应的规则将其转化为真实的DOM节点。">
<meta name="twitter:image" content="https://huangrenhao.gitee.io/2019/11/18/react-zhong-de-xu-ni-dom/render-realize.png">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Muse',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://huangrenhao.gitee.io/2019/11/18/react-zhong-de-xu-ni-dom/">





  <title>react中的虚拟DOM | renhao</title>
  








<link rel="stylesheet" href="/css/prism-tomorrow.css" type="text/css"></head>

<body itemscope="" itemtype="http://schema.org/WebPage" lang="zh-Hans">
  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope="" itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">renhao</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle"></p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br>
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope="" itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://huangrenhao.gitee.io/2019/11/18/react-zhong-de-xu-ni-dom/">

    <span hidden itemprop="author" itemscope="" itemtype="http://schema.org/Person">
      <meta itemprop="name" content="renhao">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope="" itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="renhao">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">react中的虚拟DOM</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2019-11-18T18:57:14+08:00">
                2019-11-18
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope="" itemtype="http://schema.org/Thing">
                  <a href="/categories/React/" itemprop="url" rel="index">
                    <span itemprop="name">React</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
              <span class="post-comments-count">
                <span class="post-meta-divider">|</span>
                <span class="post-meta-item-icon">
                  <i class="fa fa-comment-o"></i>
                </span>
                <a href="/2019/11/18/react-zhong-de-xu-ni-dom/#comments" itemprop="discussionUrl">
                  <span class="post-comments-count valine-comment-count" data-xid="/2019/11/18/react-zhong-de-xu-ni-dom/" itemprop="commentCount"></span>
                </a>
              </span>
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  2.1k
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  8
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>参考了这篇<a href="https://juejin.im/entry/5af99e786fb9a07ac90d5664#comment" target="_blank" rel="noopener">文章</a>学习了下，首先虚拟DOM的构成就是由在JSX组件中React.render调用时会调用React.createElement方法，这个方法会返回一个具有type、props等键值对的一个对象，这个对象就是虚拟DOM，然后<code>ReactDOM.render</code>按照相应的规则将其转化为真实的DOM节点。<a id="more"></a></p>
<p>举一个简单的例子：</p>
<pre class=" language-JSX"><code class="language-JSX"><div classname="cn">
  Content!
</div>
</code></pre>
<p>上面的JSX代码通过Bable被转化为下面这样的代码</p>
<pre><code>React.createElement(&quot;div&quot;, {
  className: &quot;cn&quot;
}, &quot;Content!&quot;);
</code></pre><p>这里有几个参数，</p>
<ul>
<li><p>首先第一个就是元素的<strong>type</strong>，对于HTML标签，例如h1、p、div等就是一个带有相应标签名称的字符串，不是HTML标签的话比如说就是用户自定义的组件名称，这个时候type就是一个引用，指向编写组件是编写的函数。</p>
</li>
<li><p>第二个参数是包含所有元素属性（<strong>attributes</strong>）的对象。可以包含className、style等等。没有的话就是一个空的对象。</p>
</li>
<li><p>剩下的参数都可以看做是元素的子元素（<strong>children</strong>），包括包含子组件的情况，或者如下面的例子</p>
<pre><code>&lt;div className=&#39;cn&#39;&gt;
  Content 1!
  &lt;br /&gt;
  Content 2!
&lt;/div&gt;
</code></pre><p>经babel转化：</p>
<pre><code>React.createElement(&quot;div&quot;, {
  className: &quot;cn&quot;
}, &quot;Content 1!&quot;, React.createElement(&quot;br&quot;, null), &quot;Content 2!&quot;);
</code></pre><p>这里的参数选择有多种类型，在例子中涉及到的就是String类型还有就是子标签会再次调用React.createElement()方法，还有其他类型的值例如基本类型（boolean 、null、undefine），或者数组</p>
</li>
</ul>
<p>这里babel转换生成createElement参数的过程可能是由正则匹配出来，然后就是createElement方法生成虚拟DOM，再通过render()方法将虚拟DOM转变成真实DOM。</p>
<h3 id="createElement-方法实现"><a href="#createElement-方法实现" class="headerlink" title="createElement()方法实现"></a>createElement()方法实现</h3><p>React.createElement的源码：</p>
<pre><code>ReactElement.createElement = function (type, config, children) {
  var propName;

  // Reserved names are extracted
  var props = {};

  var key = null;
  var ref = null;
  var self = null;
  var source = null;

  // 将参数赋给props对象
  if (config != null) {
    ref = config.ref === undefined ? null : config.ref;
    key = config.key === undefined ? null : &#39;&#39; + config.key;
    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // Remaining properties are added to a new props object
    for (propName in config) {
      // 跳过React保留的参数
      if (config.hasOwnProperty(propName) &amp;&amp; !RESERVED_PROPS.hasOwnProperty(propName)) {
        props[propName] = config[propName];
      }
    }
  }


  // 将子元素按照顺序赋给children的数组
  // Children can be more than one argument, and those are transferred onto
  // the newly allocated props object.
  var childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength &gt; 1) {
    var childArray = Array(childrenLength);
    for (var i = 0; i &lt; childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    props.children = childArray;
  }

  // 对于默认的参数，判断是否有传入值，有的话直接将参数和对应的值赋给props，否则将参数和参数默认值赋给props
  // Resolve default props
  if (type &amp;&amp; type.defaultProps) {
    var defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
};

var ReactElement = function (type, key, ref, self, source, owner, props) {
  var element = {

    // Symbol类型的tag唯一标示这个对象是一个React Element类型
    // This tag allow us to uniquely identify this as a React Element
    $$typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,

    // Record the component responsible for creating this element.
    _owner: owner
  };

  return element;
};
</code></pre><p>例子：</p>
<pre><code>const View = (
  &lt;div
    className=&#39;wrapper--outer&#39;
    &gt;
    &lt;div
      className=&#39;wrapper1--inner&#39;
      style={{ color: '#38f' }}
      &gt;
      hello world
    &lt;/div&gt;
    &lt;div
      className=&#39;wrapper2--inner&#39;
      &gt;
      hello world
      {null}
    &lt;/div&gt;
    &lt;Hello /&gt;
    &lt;Inner text=&quot;heiheihei&quot;&gt;
      &lt;div&gt;yoyoyo&lt;/div&gt;
    &lt;/Inner&gt;
  &lt;/div&gt;
);
-------------------------------------------------------------
========================babel result=========================
&#39;use strict&#39;;

var View = React.createElement(
  &#39;div&#39;,
  {
    className: &#39;wrapper--outer&#39;
  },
  React.createElement(
    &#39;div&#39;,
    {
      className: &#39;wrapper1--inner&#39;,
      style: { color: &#39;#38f&#39; }
    },
    &#39;hello world&#39;
  ),
  React.createElement(
    &#39;div&#39;,
    {
      className: &#39;wrapper2--inner&#39;
    },
    &#39;hello world&#39;
  ),
  React.createElement(Hello, null),
  React.createElement(
    Inner,
    { text: &#39;heiheihei&#39; },
    React.createElement(
      &#39;div&#39;,
      null,
      &#39;yoyoyo&#39;
    )
  )
);
-------------------------------------------------------------
=====================createElement result====================
{
  ...
  type: &#39;div&#39;,
  props: {
    className: &#39;wrapper--outer&#39;
  },
  children: [
    {
      type: &#39;div&#39;,
      props: {
        className: &#39;wrapper1--inner&#39;,
        style: {
          color: &#39;#38f&#39;
        }
      },
      children: &#39;hello world&#39;
    }, {
      type: &#39;div&#39;,
      props: {
        className: &#39;wrapper2-inner&#39;
      },
      children: [
        &#39;hello world&#39;,
        null
      ]
    },
    {
      type: Hello,
      props: null
    },
    {
      type: Inner,
      props: {
        text: &#39;heiheihei&#39;
      },
      children: [
        {
          type: &#39;div&#39;,
          props: null,
          children: &#39;yoyoyo&#39;
        }
      ]
    }
  ]
  ...
}
</code></pre><p>综合代码和源码分析可知，React.createElement方法只是返回了一个多层级的大对象，这个对象也称<code>ReactElement</code>，也就是react中的虚拟DOM节点对象，这么一个对象只是保存了DOM需要的数据，并没有对应的方法来实现React提供我们的那些功能和特性。基于ReactElement对象创建，同时注入一些方法（实现我们熟知的React特性）就构成了<code>ReactComponent</code>。还有一种<code>ReactClass</code>就如上例中的Hello和Inner那些我们在写React时候自定义的组件类，ReactClass实例化之后调用render方法可返回ReactElement。</p>
<h3 id="render方法实现"><a href="#render方法实现" class="headerlink" title="render方法实现"></a>render方法实现</h3><p><img src="/2019/11/18/react-zhong-de-xu-ni-dom/render-realize.png" alt="函数执行过程"></p>
<p>这里需要注意在react中主要有四种组件：</p>
<ul>
<li>Empty Component (空元素)</li>
<li>Text Component（文本或数字）</li>
<li>DOM Component（DOM元素）</li>
<li>ComPosite Component（自定义组件）</li>
</ul>
<p><strong>函数的执行过程</strong>：虚拟DOM节点对象和指定的DOM container 一起传进<code>ReactDOM.render</code>，接下来会调用一些内部方法，接着调用<code>instantiateReactComponent</code>，这个函数根据element的类型实例化对应的component。</p>
<ul>
<li>element类型为string时，说明是文本，创建<code>ReactDOMTextComponent</code>；</li>
<li>element类型为ReactElement时，说明是React元素，进一步判断element.type的类型<ul>
<li>当element.type为string时，为DOM原生节点，创建<code>ReactDOMComponent</code>；</li>
<li>当element.type为函数或者类时，为react组件，创建<code>ReactCompositeComponent</code>；</li>
</ul>
</li>
</ul>
<h3 id="react-diff"><a href="#react-diff" class="headerlink" title="react diff"></a>react diff</h3><p>react 16 之前：通过 render 和 setState 进行组件渲染和更新时主要有两个阶段</p>
<ul>
<li>调和阶段，reconciler，自顶向下通过递归，遍历新数据生成新的 virtual dom，通过 diff 找到需要变更的元素放到更新队列中。采用递归的方法，也即 stack reconciler，一旦任务开始就无法中断，js 会一直占用主线程，一直等到整棵树计算完成才把执行权交给渲染引擎（导致用户交互、动画等任务无法立即得到处理，影响用户体验）</li>
<li>渲染阶段，renderer，遍历更新队列，通过调用宿主环境（dom、native）api，实际更新渲染对应元素</li>
</ul>
<p>react 16 解决方案：合作式调度，将 reconciler 过程拆分成多个子任务，每次只做一小部分，做完查看是否还有剩余时间，如果没有则挂起当前任务，将时间控制权交给主线程，当主线程不忙的时候再继续执行。</p>
<blockquote>
<p>浏览器一帧需要完成的操作：</p>
<ul>
<li>处理用户的交互</li>
<li>js 解析执行</li>
<li>帧开始（窗口尺寸变更，页面滚动等处理）</li>
<li>requestAnimationFrame</li>
<li>布局</li>
<li>绘制</li>
</ul>
<p>每秒绘制帧数 fps 达到 60 时页面时流畅的，小于则会感到卡顿。每帧时间即为 1000/60 约为 16ms。</p>
</blockquote>
<p>diff 是新旧节点的对比，主要是构建 currentInWorkProgress 的过程，同时得到 Effect List，为下一阶段的 commit做准备。采用的策略是从链表头部开始比较的方法，一个节点的插入、删除、移动都是在节点树的同一层级完成。</p>
<p>children 有以下情况</p>
<ul>
<li>textNode：节点类型相同则可以复用，把剩下的节点删除。节点类型不同则从 currentFirstChild 开始删除剩下的节点</li>
<li>reactElement：通过 key 和节点类型判断节点是否可以复用，如果节点类型不同就将节点从当前节点开始把剩余的删除。还有可以找复用的节点情况，这里不同之处就是不仅凭第一个节点判断是否可以复用，会去找当前节点的兄弟节点，直到找到key 相同并且节点类型相同，否则删除所有子节点</li>
<li>array：从头部开始比较<ul>
<li>相同位置进行比较：从新旧数组中按照索引一一对比</li>
<li>新节点、老节点遍历完毕</li>
<li>移动：把所有老数组元素按 key 或者 index 放到 map，遍历新数组根据新数组的 key 或者 index 到map 中找能否复用</li>
</ul>
</li>
</ul>
<blockquote>
<p>参考</p>
<p><a href="https://www.jianshu.com/p/ff58ec9bf15d" target="_blank" rel="noopener">React 16 diff</a></p>
<p><a href="https://mp.weixin.qq.com/s/dONYc-Y96baiXBXpwh1w3A" target="_blank" rel="noopener">fiber</a></p>
</blockquote>
<p>虚拟 dom 和 diff 的意义：对两棵树进行 diff 是有性能损耗的，diff 的实际意义在于为你掩盖底层的 DOM 操作，让你用更声明式的方式来描述你的目的，从而让你的代码更容易维护。没有任何框架可以比纯手动的优化 DOM 操作更快，因为框架的 DOM 操作层需要应对任何上层 API 可能产生的操作，它的实现必须是普适的。</p>
<blockquote>
<p><a href="https://mp.weixin.qq.com/s/XR3-3MNCYY2pg6yVwVQohQ" target="_blank" rel="noopener">参考</a></p>
</blockquote>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2019/11/18/react-infinite-scroll/" rel="next" title="react优化长列表">
                <i class="fa fa-chevron-left"></i> react优化长列表
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2020/08/27/webpack-code-spliting/" rel="prev" title="webpack code spliting">
                webpack code spliting <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  
    <div class="comments" id="comments">
    </div>
  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope="" itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">renhao</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">94</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">19</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            

          </nav>

          
            <div class="feed-link motion-element">
              <a href="/atom.xml" rel="alternate">
                <i class="fa fa-rss"></i>
                RSS
              </a>
            </div>
          

          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#createElement-方法实现"><span class="nav-number">1.</span> <span class="nav-text">createElement()方法实现</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#render方法实现"><span class="nav-number">2.</span> <span class="nav-text">render方法实现</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#react-diff"><span class="nav-number">3.</span> <span class="nav-text">react diff</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">renhao</span>

  
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>
    
      <span class="post-meta-item-text">Site words total count&#58;</span>
    
    <span title="Site words total count">265.9k</span>
  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Muse</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  

  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  










  <script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
  <script src="//unpkg.com/valine/dist/Valine.min.js"></script>
  
  <script type="text/javascript">
    var GUEST = ['nick','mail','link'];
    var guest = 'nick,mail,link';
    guest = guest.split(',').filter(item=>{
      return GUEST.indexOf(item)>-1;
    });
    new Valine({
        el: '#comments' ,
        verify: false,
        notify: false,
        appId: '2P1xqUWiCoNm14MH4yhURlJi-gzGzoHsz',
        appKey: 'uJN0uagHIVgXtBO1OuLV9Ban',
        placeholder: 'Just go go',
        avatar:'mm',
        guest_info:guest,
        pageSize:'10' || 10,
    });
  </script>



  





  

  

  

  
  

  

  

  

</body>
</html>
